﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Xml.Linq;
using Messaging2;
using System.Reflection;
using ExcelDna.Integration;
using ExcelDna.Integration.Rtd;
using Logger;
using u = FinPlusUtility.Utility;
using log = Logger.Logger;

namespace FinPlusRTDMessage
{
    [Guid("e7cc6bc2-27c6-4fd4-949e-54219bc8fada"), ProgId("fpmessage")]
    public class FinPlusRTDMessage : IRtdServer
    {
        private static ITransport _tran;
        private static ConcurrentDictionary<string, IMessage> _messages;
        private ConcurrentDictionary<int, string> _topics;
        private static u.Env _env = u.Env.unknown;
        private static u.Config _config;
        private IRTDUpdateEvent _callback;
        private static Object _lock = new Object();

        //public
        [ExcelFunction(Description = "listen real time data", Category = "FinPlus.RTD")]
        public static object FinPlusRTD(string Topic)
        {
            try
            {
                object res = XlCall.Excel(XlCall.xlfRtd, "fpmessage", null, Topic);
                return res;
            }
            catch (Exception e)
            {
                return e;
            }
        }

        [ExcelFunction(Description = "send data", Category = "FinPlus.RTD")]
        public static string FinPlusSend(string Topic, object Value)
        {
            try
            {
                var valueType = Value.GetType().Name;
                IMessage msg = null;
                switch (valueType)
                {
                    case "String": msg = new Message(Value.ToString(), Topic); break;
                    case "Double": msg = new Message(double.Parse(Value.ToString()), Topic); break;
                    case "Object[]": msg = new Message((object[])Value, Topic); break;
                    case "Object[,]": msg = new Message((object[,])Value, Topic); break;
                    default: new Message(Value.ToString(), Topic); break;
                }

                Connect();
                if (msg != null) _tran.Publish(msg);

                return Topic;
            }
            catch (Exception e)
            {
                return e.ToString();
            }
        }

        public void AutoClose()
        {

        }

        public void AutoOpen()
        {
            ExcelDna.ComInterop.ComServer.DllRegisterServer();
        }

        public object ConnectData(int topicId, ref Array strings, ref bool newValues)
        {
            try
            {
                if (1 != strings.Length) 
                    return "one parameter is required";

                string topic = strings.GetValue(0).ToString();
                _topics[topicId] = topic;
                _tran.Listen(topic, true);

                return GetMessage(topic);
            }
            catch (Exception e)
            {
                return e.ToString();
            }
        }

        public void DisconnectData(int topicId)
        {
           // _topics.Remove(topicId);
            //TODO still has topic in list of listener topis so needs to be remove pos prob might have two funs listen to same topis
            //bit confusing with topics and topicids they are not related
        }

        public int Heartbeat()
        {
            return 1;
        }

        public Array RefreshData(ref int topicCount)
        {
            object[,] data = new object[2, _topics.Count];

            try
            {
               
                int index = 0;

                foreach (int topicId in _topics.Keys)
                {
                    data[0, index] = topicId;
                    data[1, index] = GetMessage(_topics[topicId]);
                    ++index;
                }

                topicCount = _topics.Count;
            }
            catch
            {
                throw;
            }

            return data;
        }

        public int ServerStart(IRTDUpdateEvent callback)
        {
            try
            {
                _callback = callback;
                _messages = new ConcurrentDictionary<string, IMessage>();
                _topics = new ConcurrentDictionary<int, string>();

                Connect();

                _tran.Listener.MessageRecieved += Callback;
            }
            catch
            {
                return 0;
            }

            return 1;
        }

        public void ServerTerminate()
        {
            //if (null != _tran)
            //{
            //    _tran.Dispose();
            //    _tran = null;
            //}
        }

        //private
        private static void Connect()
        {
            try
            {
                //connect tran load config and initialize logger
                if (_config != null) return;

                lock (_lock)
                {
                    if (_config != null) return;

                    var p = u.GetConfigPath(Assembly.GetExecutingAssembly());
                    _config = u.ConfigXml(p, out _env);
                    log.Initialise(p, _env.ToString());

                    _tran = new Transport(_config["messagingsettings"]["address"], int.Parse(_config["messagingsettings"]["port"]));
                }
            }
            catch
            {

            }
        }

        private static string GetMessage(string topic)
        {
            if (!_messages.ContainsKey(topic))
                return "Waiting!";
            return _messages[topic].ToString();
        }

        //events
        private void Callback(object sender, EventArgs a)
        {
            MessageEventArgs msg = (MessageEventArgs)a;
            _messages[msg.Message.Topic] = msg.Message;
            _callback.UpdateNotify();
        }
    }
}

